package com.hwtx.form.domain.handle.datamodel;

import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.hwtx.form.domain.FormConstants;
import com.hwtx.form.domain.repo.DataModelRepo;
import com.hwtx.form.domain.vo.DataModelRelateVo;
import com.hwtx.form.persistence.DataSourceRepo;
import com.hwtx.form.util.JacksonUtil;
import io.geekidea.boot.framework.exception.BusinessException;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.Serial;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static com.hwtx.form.domain.handle.datamodel.DataModel.parserAndBuildDetails;

/**
 * <p>
 * 数据模型关联表
 * </p>
 *
 * @author baomidou
 * @since 2024-10-23
 */
@Getter
@Setter
@TableName("data_model_relate")
public class DataModelRelate implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 模型左侧名称
     */
    @TableField("first_name")
    private String firstName;
    /**
     * 模型右侧名称
     */
    @TableField("second_name")
    private String secondName;

    /**
     * 关联类型
     */
    @TableField("join_type")
    private String joinType;

    /**
     * 关联字段
     */
    @TableField("join_fields")
    @JsonDeserialize(using = Array2StringDeserializer.class)
    @JsonSerialize(using = String2ListDeserializer.class)
    private String joinFields;

    /**
     * 数据模型名称
     */
    @TableField("model_name")
    private String modelName;
    /**
     * 过滤条件
     */
    @TableField("filter")
    @JsonDeserialize(using = Array2StringDeserializer.class)
    @JsonSerialize(using = String2ListDeserializer.class)
    @Deprecated
    private String filters;

    /**
     * 创建时间
     */
    @TableField(value = "create_time", fill = FieldFill.INSERT)
    @JsonIgnore
    private Date createTime;

    /**
     * 创建人ID
     */
    @TableField(value = "create_id", fill = FieldFill.INSERT)
    @JsonIgnore
    private String createId;

    /**
     * 修改时间
     */
    @TableField(value = "update_time", fill = FieldFill.UPDATE)
    @JsonIgnore
    private Date updateTime;

    /**
     * 更新人ID
     */
    @TableField(value = "update_id", fill = FieldFill.UPDATE)
    @JsonIgnore
    private String updateId;


    public static Set<String> getRelateTables(List<DataModelRelate> relates) {
        Set<String> tables = Sets.newHashSet();
        relates.forEach(relate -> {
            tables.add(relate.getFirstName());
            tables.add(relate.getSecondName());
        });
        return tables;
    }

    public static List<DataModelRelateVo> listRelate(DataModelBuildInfo buildInfo) {
        String modelName = buildInfo.getModelName();
        if (StringUtils.isBlank(modelName)) {
            throw new IllegalArgumentException("模型名称不能为空");
        }
        DataModelRepo dataModelRepo = SpringUtil.getBean(DataModelRepo.class);
        List<DataModelRelate> relates = dataModelRepo.getRelates(modelName);
        Map<String, Integer> numMap = new HashMap<>();
        DataModel dataModel = dataModelRepo.getDataModel(modelName);
        String master;
        if (dataModel != null) {
            if (!Objects.equals(dataModel.getMaster(), buildInfo.getMaster())
                    || !Objects.equals(dataModel.getDs(), buildInfo.getDs())) {
                return Lists.newArrayList();
            }
            master = dataModel.getMaster();
        } else {
            master = buildInfo.getMaster();
        }
        if (StringUtils.isBlank(master)) {
            throw new BusinessException("主模型名称不能为空");
        }
        AtomicInteger num = new AtomicInteger(1);
        return relates.stream().map(relate -> {
            DataModelRelateVo relateVo = new DataModelRelateVo();
            relateVo.setId(relate.getId());
            relateVo.setModelName(relate.getModelName());
            List<Map<String, String>> fields = JacksonUtil.json2Array(relate.getJoinFields());
            relateVo.setJoinFields(Joiner.on(",").join(fields.stream().map(field -> field.get("firstField")).collect(Collectors.toList())));
            relateVo.setJoinType(relate.getJoinType());
            if (!numMap.containsKey(relate.getSecondName())) {
                numMap.put(relate.getSecondName(), num.getAndIncrement());
            }
            relateVo.setSecondName(relate.getSecondName() + "[" + numMap.get(relate.getSecondName()) + "]");
            if (Objects.equals(master, relate.getFirstName())) {
                relateVo.setFirstName(relate.getFirstName() + "[0]");
            } else {
                if (!numMap.containsKey(relate.getFirstName())) {
                    numMap.put(relate.getFirstName(), num.getAndIncrement());
                }
                relateVo.setSecondName(relate.getSecondName() + "[" + numMap.get(relate.getSecondName()) + "]");
            }
            return relateVo;
        }).toList();
    }

    public static List<DataModelDetail> buildDetails(DataModelBuildInfo buildInfo, DataSourceRepo dataSourceRepo) {
        if (StringUtils.isBlank(buildInfo.getModelName())) {
            return Lists.newArrayList();
        }
        DataModelRepo dataModelRepo = SpringUtil.getBean(DataModelRepo.class);
        DataModel dataModel = dataModelRepo.getDataModel(buildInfo.getModelName());
        if (dataModel == null) {
            throw new BusinessException("模型不存在,先保存模型配置");
        }

        List<DataModelDetail> details;
        if (StringUtils.isNotBlank(buildInfo.getSql()) && !Objects.equals(dataModel.getSqlContent(), buildInfo.getSql())) {
            details = parserAndBuildDetails(buildInfo, dataSourceRepo);
        } else {
            details = DataModel.buildDataModelDetail(buildInfo).getData();
            if (details.isEmpty() && StringUtils.isNotEmpty(buildInfo.getSql())) {
                details = parserAndBuildDetails(buildInfo, dataSourceRepo);
            }
        }
        String master = dataModel.getMaster();
        AtomicInteger i = StringUtils.isBlank(master) ? new AtomicInteger(0) : new AtomicInteger(1);
        Map<String, Integer> num = new HashMap<>();
        return details.stream().peek(detail -> {
            if (Objects.equals(detail.getTableName(), master)) {
                detail.setTableName(detail.getTableName() + "[0]");
            } else {
                if (StringUtils.isNotBlank(detail.getTableName())) {
                    if (!num.containsKey(detail.getTableName())) {
                        num.put(detail.getTableName(), i.getAndIncrement());
                    }
                    detail.setTableName(detail.getTableName() + "[" + num.get(detail.getTableName()) + "]");
                }
            }
        }).collect(Collectors.toList());
    }

    @Data
    public static class RelateField {
        private String firstField;
        private String secondField;
    }

    static class Array2StringDeserializer extends JsonDeserializer<String> {
        @Override
        public String deserialize(JsonParser p, DeserializationContext ctxt)
                throws IOException {
            List<?> rawData = p.readValueAs(List.class);
            return JacksonUtil.object2json(rawData);
        }
    }

    static class String2ListDeserializer extends JsonSerializer<String> {
        @Override
        public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeObject(JacksonUtil.json2Array(value));
        }
    }

    static class String2OperatorType extends JsonDeserializer<FormConstants.OperatorType> {
        @Override
        public FormConstants.OperatorType deserialize(JsonParser p, DeserializationContext ctxt)
                throws IOException {
            String value = p.getValueAsString();
            return FormConstants.OperatorType.of(value);
        }
    }
}
